#include "STEClasses.h"
#include "Value.h"
#include "ParserUtil.h"

/****************************************************************/
const Type* GlobalEntry::typeCheck() {

    SymTabEntry::typeCheck();

    /*if (symTab()) {
		SymTab::iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ((*it) && (*it)->name() != "any") {
			   	(*it)->typeCheck();
			}
		}
	}*/
    vector<RuleNode*>::const_iterator it = rules_.begin();
	for (;it!=rules_.end();++it) {
		if ((*it))
			(*it)->typeCheck();
	}

    }
void GlobalEntry::typePrint(ostream& os, int indent) const {
    prtln(os,indent+STEP_INDENT);
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ((*it) && (*it)->name() != "any") {
				(*it)->typePrint(os,indent+STEP_INDENT);
			}
		}
	}
	// Rules
	vector<RuleNode*>::const_iterator it = rules_.begin();
	for (;it!=rules_.end();++it) {
		if ((*it))
			(*it)->typePrint(os,indent+STEP_INDENT);
	}
	prtln(os,indent);
	prtln(os,indent);



    }
/****************************************************************/
const Type* EventEntry::typeCheck() {/* Fill in yours */}
void EventEntry::typePrint(ostream& out, int indent) const {
    const Type* t = type();
	out << "event " << name();

	if (symTab() == NULL) {
		out << ";";prtln(out,indent);
		return;
	} else {
		out << "(";
	}
	SymTab::const_iterator it = symTab()->begin();
	for (;it!=symTab()->end();) {
		if ((*it)) {
			(*it)->type()->print(out,indent);
			out << " " << (*it)->name();
		}
		if ( (++it) != symTab()->end() )
			out << ", ";
	}
	out << ");";
	prtln(out,indent);



    }

const Type* BlockEntry::typeCheck() {/* Fill in yours */}
void BlockEntry::typePrint(ostream&, int indent) const {/* Fill in yours */}



const Type* FunctionEntry::typeCheck() {

	formals_= new   vector<const VariableEntry*>();


	if(symTab())
	{
		for(SymTab::iterator si =symTab()->begin(); si!=symTab()->end(); ++si)
		{
			VariableEntry * ste=(VariableEntry * )(*si);
	 		if (ste && ste-> varKind()==VariableEntry:: PARAM_VAR)
	 		{
	 			formals_->push_back(ste);
	 		}
	 		//added for type check of local variable
	 		else if(ste && ste-> varKind()==VariableEntry:: LOCAL_VAR)
	 		{
	 			ste->typeCheck();
	 		}

        }




	}

	if(body())
	{

		body()->typeCheck();
	}



    }
void FunctionEntry::typePrint(ostream& out, int indent) const {
    const Type* retT = type()->retType();
	out << retT->name();
	out << " " << name() << "(";
	// Parameters
	string params;
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) && (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() == VariableEntry::PARAM_VAR) {
					if (params.empty()) params += var->type()->fullName() + " " + var->name();
					//else params += ", " + var->type()->name();
					else params += ", " + var->type()->fullName() + " " + var->name();
				}
			}
		}
	}
	out << params << ")";
	bool declExist = false;
	if (symTab()) {
		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) && (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() != VariableEntry::PARAM_VAR) {
					declExist = true;
					break;
				}
			}
		}
	}
	if (declExist || body()) {
		out << " {";
		prtln(out,indent + STEP_INDENT);

		SymTab::const_iterator it = symTab()->begin();
		for (;it!=symTab()->end();++it) {
			if ( (*it) &&  (*it)->kind() == SymTabEntry::VARIABLE_KIND) {
				VariableEntry* var = (VariableEntry*)(*it);
				if (var->varKind() != VariableEntry::PARAM_VAR) {
					out  << var->type()->name() << ";";
					prtln(out,indent + STEP_INDENT);
				}
			}
		}
		if (body()) {
			body()->typePrintWithoutBraces(out,indent+STEP_INDENT);
		}
		out << "}";
		prtln(out,indent);
	} else {
		out << ";";
		prtln(out,indent);
	}


    }

const Type* VariableEntry::typeCheck() {
    const Type *te=NULL;
    if (initVal()) {
		te = initVal()->typeCheck();

	}
	if(te && type()) {

	    if(te->fullName() == type()->fullName()){ }
	    else if((te->isSubType(*(type())))) {
        //else if((type()->isSubType(*(te)))) {

	        //if(!(Type::isInt(te->tag()) && Type::isInt(type()->tag())))
	        //initVal()->coercedType(type());

	        }
	    else
            errMsg("Assignment between incompatible types", line(), column(),file().c_str());
        }
    }
void VariableEntry::typePrint(ostream& os, int indent) const {
    string typeName = type()->name();
	if (type()->tag()==Type::CLASS) {
		typeName += " " + type()->typeDesc()->name();
	}
	if (initVal()) {
		os << typeName << " " << name() << " = ";
		initVal()->typePrint(os,indent);
		//os << ";";
	}
	else
		os << typeName << " " << name();
	prtln(os, indent);


    }

const Type* ClassEntry::typeCheck() {}
void ClassEntry::typePrint(ostream& os, int indent) const {
   os << "class " << name() << ";";
	prtln(os, indent);

    }


